React Hooks 的運作機制是建立在 React Fiber 架構之上,所有的 Hook 狀態會以 linked list 的方式儲存。fiber 中的 memorizedState
就是用來保存 hooks 資料的地方,不同的 Hook 會儲存不同的資料,但基本的儲存和處理流程是類似的。
這樣的 linked list 結構確保每個 component 渲染時,React 能夠依照 Hook 的調用順序來讀取和更新對應的狀態。React 在初次渲染和後續更新過程中會執行不同的邏輯來處理 Hook。
當一個組件第一次渲染時,React 會創建一條新的 linked list 來儲存每個 Hook 的狀態。每次呼叫一個 Hook,React 會創建對應的 Hook 狀態物件,並依次將它們連接起來。
React 透過 workInProgressHook
這個指標來追蹤當前處理的 Hook。每當你呼叫一個新的 Hook,這個指標會移動到下一個 Hook 狀態,並將新 Hook 串在 linked list 上,這樣 React 可以依序處理每個 Hook 的邏輯。
Hook 狀態物件的屬性有這些:
memorizedState
: 根據不同類型的 hook 保存對應的資料或狀態baseState
: 本次更新前 Fiber 節點的狀態baseQueue
: 在此次更新前的狀態更新隊列queue
: 儲存本次更新中待處理的狀態更新隊列next
: 只向下一個 Hook 物件
在組件後續更新時,React 不會重新創建 Hook 的 linked list,而是根據上一次渲染的內容進行更新。React 會使用一個指標來追蹤每個 Hook,逐一更新它們的狀態。
React 透過 Hook 的調用順序來追蹤每個 component 的 狀態。當 Hook 被放入條件判斷中,會造成每次渲染時 Hook 呼叫順序的不一致,進而導致 React 錯誤管理這些狀態。例如,下面的程式碼:
function Component() {
const [state1, setState1] = useState(false); //狀態1
if (state1) {
const [state3, setState3] = useState(0); //狀態3
}
const [state2, setState2] = useState(0); //狀態2
return (
<div>
<button onClick={() => setState1(true)}>Click</button>
<p>{state1}</p>
<p>{state2}</p>
</div>
);
}
當 state1 是 false
: 只會調用兩個 Hook(state1 和 state2),
當 state1 是 true
: useState 會多出一個新的 Hook(state3),會把 state2 的狀態賦值給 state3。
Hook 的調用和更新都依賴於 fiber 的存在。如果在普通的 function 中使用 Hook,React 無法將這些 Hook 對應到 fiber 上的正確位置,這會導致無法正確追蹤狀態並引發錯誤。
參考資料:
https://www.youtube.com/watch?v=cxbVN1F-aag
https://medium.com/@ryardley/react-hooks-not-magic-just-arrays-cd4f1857236e
https://juejin.cn/post/6968820472431509535
https://segmentfault.com/a/1190000039076330